package web

import (
	"fmt"
	"math/cmplx"
	"net/http"
	"reflect"
	"strconv"

	"gitee.com/thubcc/mobile/utils"
	"gitee.com/thubcc/rtlmmap"
	"github.com/gin-gonic/gin"
	"github.com/prometheus/common/log"
)

type View struct {
	rtlmmap.FTView
	Mode string `json:"mode"`
}

func (view *View) GetTuner(c *gin.Context) {
	ftco := view.View.GetFTco()
	p, d := ftco.GetTuner()
	c.JSON(http.StatusOK, tuner{p, d})
}

func (view *View) SetPPM(c *gin.Context) {
	ftco := view.GetFTco()
	ftco.InitPPM()
	sp := c.Query("ppmf")
	if sp != "" {
		if p, err := strconv.ParseFloat(sp, 64); err == nil {
			ftco.PPMF(p, false)
		}
	}
	sp = c.Query("ppmt")
	if sp != "" {
		if p, err := strconv.ParseFloat(sp, 64); err == nil {
			ftco.PPMT(p)
		}
	}
	sp = c.Query("ppm")
	if sp != "" {
		if p, err := strconv.ParseFloat(sp, 64); err == nil {
			ftco.PPMAbs(p)
		}
	}
	p, d := ftco.GetTuner()
	c.JSON(http.StatusOK, tuner{p, d})
}

func (view *View) Spectrum(c *gin.Context) {
	sr := 1.92e6
	sres := c.DefaultQuery("resolution", "10000")
	res, _ := strconv.ParseFloat(sres, 64)
	point := int(sr / res)
	fft := utils.NewFFT(point)
	ffto := make([]float64, point)
	view.Reset()
	rfd := view.Raw(0, 100)
	for i := 0; i < len(rfd); i += point {
		copy(fft.Buf, rfd[i:i+point])
		fft.Execute()
		for i, v := range fft.Buf {
			ffto[i] += real(v * cmplx.Conj(v))
		}
	}
	c.JSON(http.StatusOK, Response{ffto, nil})
}

type Views struct {
	views     map[int]*View
	needReset bool
}

func NewViews(nr bool) *Views {
	return &Views{views: make(map[int]*View), needReset: nr}
}

func (vs *Views) List(c *gin.Context) {
	c.JSON(http.StatusOK, Response{vs.views, nil})
}

func (vs *Views) checkView(c *gin.Context) (view *View, err error) {
	port := Str2int(c.Param("port"))
	var ok bool
	if view, ok = vs.views[int(port)]; !ok {
		err = fmt.Errorf("Can not find view %d", port)
		c.JSON(http.StatusInternalServerError, Response{Data: nil, Error: err})
	}
	return
}

func (vs *Views) PortSpecificationAPI(name string) func(c *gin.Context) {
	return func(c *gin.Context) {
		if view, err := vs.checkView(c); err == nil {
			getValue := reflect.ValueOf(view)
			methodValue := getValue.MethodByName(name)
			args := []reflect.Value{reflect.ValueOf(c)}
			methodValue.Call(args)
		} else {
			c.JSON(http.StatusOK, Response{nil, err})
		}
	}
}

func (vs *Views) NewView(c *gin.Context) {
	port := Str2int(c.Param("port"))
	log.Info("New view ", port)
	if _, ok := vs.views[int(port)]; ok {
		force := c.DefaultQuery("force", "false")
		if force == "false" {
			c.JSON(http.StatusOK, Response{"View has aready installed", nil})
			return
		}
	}
	mode := c.DefaultQuery("mode", "lte")
	var config *rtlmmap.Config
	switch mode {
	case "lte":
		config = &rtlmmap.DefaultLTEConfig
	case "fm":
		config = &rtlmmap.DefaultFMConfig
	default:
		config = &rtlmmap.DefaultLTEConfig
	}
	rv := rtlmmap.NewRtlView(port, config).FTV(port)
	if vs.needReset {
		rv.ResetPPM()
	}
	vs.views[port] = &View{*rv, mode}
	c.JSON(http.StatusOK, Response{"View installing", nil})
}

func (vs *Views) GetView(port int) (v *View, ok bool) {
	v, ok = vs.views[port]
	return
}

func NewViewWithHandle(c *gin.Context) (v *View) {
	port, config := CommonParam(c)
	ftv := rtlmmap.NewRtlView(port, config).FTV(port)
	v.ResetPPM()
	c.JSON(http.StatusOK, Response{"View installing", nil})
	return &View{*ftv, c.Query("mode")}
}
